﻿using Framework.Models;
using Framework.Models.Entities;
using Framework.Models.Request;
using Framework.Repository;
using Framework.Utility;
using Framework.Utility.Extensions;
using Framework.Utility.Helper;
using Framework.Utility.ViewModels;
using System;
using System.Reflection;

namespace Framework.Code.FlowWork;

public class FlowHandle
{
    private readonly IFlowinstanceRepository _flowinstanceRepository;
    private readonly IApprovalRecordRepository _approvalRecordRepository;
    private readonly IFlowinstanceOperationHistoryRepository _flowOptionRepository;
    private readonly IFlowinstanceTransitionHistoryRepository _flowTransitionRepository;

    private static UserCacheModel user = new UserCacheModel()
    {
        UserId = "1",//1301
        UserName = "admin"  //张三
    };

    //private static UserCacheModel user
    //{
    //    //get { return CacheFactory.Cache.Get<UserCacheModel>(CacheKay.SessionUser); }
    //}

    public FlowHandle(IFlowinstanceRepository flowinstanceRepository,
        IApprovalRecordRepository approvalRecordRepository,
        IFlowinstanceOperationHistoryRepository flowOptionRepository,
        IFlowinstanceTransitionHistoryRepository flowTransitionRepository)
    {
        _flowinstanceRepository = flowinstanceRepository;
        _approvalRecordRepository = approvalRecordRepository;
        _flowOptionRepository = flowOptionRepository;
        _flowTransitionRepository = flowTransitionRepository;
    }

    /// <summary>
    /// 审核流程
    /// </summary>
    public void Approval(ApprovalReqest request)
    {
        bool isReject = TagState.Reject.Equals(request.ApprovalReslut);
        if (isReject) //驳回
        {
            NodeReject(request);
        }
        else
        {
            NodeApproval(request);
        }
    }

    /// <summary>
    /// 节点审核
    /// </summary>
    /// <param name="instanceId"></param>
    /// <returns></returns>
    public void NodeApproval(ApprovalReqest request)
    {
        var instanceId = request.FlowInstanceId;

        Fw_Flowinstance flowInstance = _flowinstanceRepository.GetByKey(instanceId);

        if (user.UserId != "1" && !flowInstance.MakerList.Contains(user.UserId))
        {
            throw new Exception("当前用户没有审批该节点权限");
        }
        var optTag = new OptionResultTag
        {
            UserName = user.UserName,
            UserId = user.UserId,
            Taged = request.ApprovalReslut,
            TagedTime = DateTime.Now
        };
        FlowRuntime wfruntime = new FlowRuntime(flowInstance);

        _flowinstanceRepository.UnitOfWork.BeginTransaction();

        #region 会签

        if (wfruntime.counterSign) //当前节点是会签节点
        {
            int confluenceRes = wfruntime.NodeConfluence(wfruntime.currentNodeId, optTag);

            if (confluenceRes == FlowNodeStatus.Join) //会签结束
            {
                if (optTag.Taged == TagState.No)
                {
                    flowInstance.IsFinish = FlowInstanceStatus.Disagree;
                    //返回给申请人
                    CheckRunToApplyUser(flowInstance, wfruntime);

                    optTag.Description = $"【审核不同意】流程结束,【{wfruntime.nodeName}】【{DateTime.Now.ToString("yyyy-MM-dd HH:mm")}】" + ",备注：" + request.ApprovalRemark;
                }
                else
                {
                    //流转到下一步
                    CheckRunToNextApprovalNode(flowInstance, wfruntime);

                    flowInstance.ConfluenceOk = 0;
                    flowInstance.ConfluenceNo = 0;
                    flowInstance.IsFinish = wfruntime.nextNodeId != Define.FlowEndCode ? FlowInstanceStatus.Running : FlowInstanceStatus.Finished;

                    optTag.Description = $"【会签审核通过】流程继续进行,【{wfruntime.nodeName}】【{DateTime.Now.ToString("yyyy-MM-dd HH:mm")}】" + ",备注：" + request.ApprovalRemark;
                }
                AddTransHistory(wfruntime);
            }
            else //会签开始
            {
                flowInstance.ConfluenceOk = wfruntime.confluenceOk;
                flowInstance.ConfluenceNo = wfruntime.confluenceNo;
                optTag.Description = $"【会签审核开始】流程继续进行,【{wfruntime.nodeName}】【{DateTime.Now.ToString("yyyy-MM-dd HH:mm")}】" + ",备注：" + request.ApprovalRemark;
            }
        }

        #endregion 会签

        #region 一般审核

        else
        {
            if (optTag.Taged == TagState.Ok)
            {
                flowInstance.IsFinish = wfruntime.nextNodeId != Define.FlowEndCode ? FlowInstanceStatus.Running : FlowInstanceStatus.Finished;
                //流转到下一步
                CheckRunToNextApprovalNode(flowInstance, wfruntime);
                string flowStatus = flowInstance.IsFinish == FlowInstanceStatus.Running ? "流程继续进行" : "流程结束";

                optTag.Description = $"【审核通过】{flowStatus},【{wfruntime.nodeName}】【{DateTime.Now.ToString("yyyy-MM-dd HH:mm")}】" + ",备注：" + request.ApprovalRemark;
            }
            else if (optTag.Taged == TagState.No)
            {
                flowInstance.IsFinish = FlowInstanceStatus.Disagree; //表示该节点不同意
                //返回给申请人
                CheckRunToApplyUser(flowInstance, wfruntime);

                optTag.Description = $"【审核不同意】流程结束,【{wfruntime.nodeName}】【{DateTime.Now.ToString("yyyy-MM-dd HH:mm")}】" + ",备注：" + request.ApprovalRemark;
            }
            AddTransHistory(wfruntime);
        }

        #endregion 一般审核

        //flowInstance.FrmData = wfruntime.FrmData;
        flowInstance.UpdateBy = user.UserName;
        flowInstance.UpdateTime = DateTime.Now;

        _flowinstanceRepository.Update(flowInstance);
        AddOperationHistory(wfruntime, optTag);
        AddApprovalRecordHistory(wfruntime, optTag, optTag => { optTag.Description = request.ApprovalRemark; });
        _flowinstanceRepository.UnitOfWork.Commit();
        ////给流程创建人发送通知信息
        //    _messageApp.SendMsgTo(flowInstance.CreateUserId,
        //        $"你的流程[{flowInstance.CustomName}]已被{user.Name}处理。处理情况如下:{flowInstanceOperationHistory.Content}");
        //wfruntime.NotifyThirdParty(_httpClientFactory.CreateClient(), tag);

        if (flowInstance.IsFinish == FlowInstanceStatus.Finished)
        {
            //处理流程完成后的业务流程
        }
        if (flowInstance.IsFinish == FlowInstanceStatus.Rejected || flowInstance.IsFinish == FlowInstanceStatus.Disagree)
        {
            //处理流程完成后的业务流程
        }
    }

    /// <summary>
    /// 驳回
    /// 返回到上一步审批人
    /// </summary>
    /// <returns></returns>
    public void NodeReject(ApprovalReqest reqest)
    {
        Fw_Flowinstance flowInstance = _flowinstanceRepository.GetByKey(reqest.FlowInstanceId);
        if (user.UserId != "1" && !flowInstance.MakerList.Contains(user.UserId))
        {
            throw new Exception("当前用户没有驳回该节点权限");
        }
        FlowRuntime wfruntime = new FlowRuntime(flowInstance);

        wfruntime.nextNodeId = flowInstance.PreviousId;
        wfruntime.nextNode = wfruntime.Nodes[flowInstance.PreviousId];

        flowInstance.ActivityId = flowInstance.PreviousId;
        flowInstance.ActivityType = (int)wfruntime.GetNodeType(flowInstance.PreviousId);
        flowInstance.ActivityName = wfruntime.Nodes[flowInstance.PreviousId].nodeName;
        flowInstance.MakerList = GetNodeMarkers(wfruntime.Nodes[flowInstance.PreviousId], flowInstance.ApplyUserId);

        flowInstance.PreviousId = wfruntime.currentNodeId;
        OptionResultTag optionTag = new()
        {
            UserName = user.UserName,
            UserId = user.UserId,
            Taged = reqest.ApprovalReslut,
            TagedTime = DateTime.Now,
            Description = $"【审核驳回】流程返回到上一步审批人，【{wfruntime.nodeName}】【{DateTime.Now.ToString("yyyy-MM-dd HH:mm")}】" + ",备注：" + reqest.ApprovalRemark,
        };

        flowInstance.UpdateBy = user.UserName;
        flowInstance.UpdateTime = DateTime.Now;

        _flowinstanceRepository.UnitOfWork.BeginTransaction();
        _flowinstanceRepository.Update(flowInstance);
        AddTransHistory(wfruntime); //工作流程流转记录
        AddOperationHistory(wfruntime, optionTag);   //操作记录
        _flowinstanceRepository.UnitOfWork.Commit();

        //给流程创建人发送通知信息
        //SendMsgTo(flowInstance.CreateUserId, $"你的流程[{flowInstance.CustomName}]已被{user.Name}驳回。备注信息:{reqest.VerificationOpinion}");
    }

    /// <summary>启动流程</summary>
    /// <remarks> 通常是对状态为【草稿】的流程进行操作，进入运行状态 </remarks>
    public void Start(string flowInstanceId)
    {
        Fw_Flowinstance flowInstance = _flowinstanceRepository.GetByKey(flowInstanceId);
        var wfruntime = new FlowRuntime(flowInstance);
        OptionResultTag optionTag = new()
        {
            UserName = user.UserName,
            UserId = user.UserId,
            Taged = TagState.Ok,
            TagedTime = DateTime.Now,
            Description = $"【流程启动】流程进行，由用户{user.UserName}启动，【{wfruntime.nodeName}】【{DateTime.Now.ToString("yyyy-MM-dd HH:mm")}】",
        };
        flowInstance.ActivityId = wfruntime.nextNodeId;
        flowInstance.ActivityType = wfruntime.nextNode?.nodeType ?? (int)FlowNodeType.End;
        flowInstance.ActivityName = wfruntime.nextNode?.nodeName ?? Define.FlowEndTitle;
        flowInstance.PreviousId = wfruntime.currentNodeId;

        flowInstance.MakerList = GetNextMakers(wfruntime, wfruntime.nextNodeId);
        flowInstance.IsFinish = wfruntime.nextNodeId != Define.FlowEndCode ? FlowInstanceStatus.Running : FlowInstanceStatus.Finished;

        _flowinstanceRepository.UnitOfWork.BeginTransaction();

        _flowinstanceRepository.Update(flowInstance);

        AddTransHistory(wfruntime);

        AddOperationHistory(wfruntime, optionTag);

        _flowinstanceRepository.UnitOfWork.Commit();
    }

    /// <summary>
    /// 召回流程
    /// </summary>
    /// <param name="flowInstanceId">流程实例id</param>
    public void ReCall(string flowInstanceId)
    {
        Fw_Flowinstance flowInstance = _flowinstanceRepository.GetByKey(flowInstanceId);
        FlowRuntime wfruntime = new FlowRuntime(flowInstance);
        OptionResultTag optionTag = new()
        {
            UserName = user.UserName,
            UserId = user.UserId,
            Taged = TagState.No,
            TagedTime = DateTime.Now,
            Description = $"【流程召回】流程返回到开始节点，【{wfruntime.nodeName}】【{DateTime.Now.ToString("yyyy-MM-dd HH:mm")}】",
        };
        string startNodeId = wfruntime.startNodeId; //起始节点

        flowInstance.IsFinish = FlowInstanceStatus.Draft;
        flowInstance.PreviousId = flowInstance.ActivityId;
        flowInstance.ActivityId = startNodeId;
        flowInstance.ActivityType = (int)wfruntime.GetNodeType(startNodeId);
        flowInstance.ActivityName = wfruntime.Nodes[startNodeId].nodeName;
        flowInstance.MakerList = flowInstance.ApplyUserId;

        _flowinstanceRepository.UnitOfWork.BeginTransaction();
        _flowinstanceRepository.Update(flowInstance);
        AddTransHistory(wfruntime);
        AddOperationHistory(wfruntime, optionTag);
        _flowinstanceRepository.UnitOfWork.Commit();
    }

    #region 获取节点的流程审核者

    /// <summary>
    /// 寻找下一步的执行人
    /// 一般用于本节点审核完成后，修改流程实例的当前执行人，可以做到通知等功能
    /// </summary>
    /// <returns></returns>
    public string GetNextMakers(FlowRuntime wfruntime, string nodeId)
    {
        var approvalNode = wfruntime.Nodes[nodeId];
        return GetNodeMarkers(approvalNode, wfruntime.applyUserId);
    }

    /// <summary>
    /// 寻找下一步的执行人
    /// 一般用于本节点审核完成后，修改流程实例的当前执行人，可以做到通知等功能
    /// </summary>
    /// <returns></returns>
    public string GetNextMakers(FlowRuntime wfruntime)
    {
        if (wfruntime.nextNodeId == Define.FlowEndCode) { return string.Empty; }

        string approvalNextId = wfruntime.GetNextApprovalNodeId(wfruntime.currentNodeId);

        if (approvalNextId.IsNullOrWhiteSpace()) { return string.Empty; }

        var approvalNode = wfruntime.Nodes[approvalNextId];

        return GetNodeMarkers(approvalNode, wfruntime.applyUserId);
    }

    /// <summary>
    /// 寻找该节点执行人
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    private string GetNodeMarkers(FlowNode node, string flowinstanceCreateUserId = "")
    {
        string makerList = "";
        if (node.nodeType == (int)FlowNodeType.Start && !string.IsNullOrEmpty(flowinstanceCreateUserId)) //如果是开始节点，通常情况下是驳回到开始了
        {
            makerList = flowinstanceCreateUserId;
        }
        else if (!string.IsNullOrWhiteSpace(node.properties.assigneeType))
        {
            if (node.properties.assigneeType == ApprovalType.all) //所有成员
            {
                makerList = ApprovalType.all;
            }
            else if (node.properties.assigneeType == ApprovalType.user) //指定成员
            {
                makerList = GenericHelpers.ArrayToString(node.properties.approvers.Select(c => c.key), makerList);
            }
            else if (node.properties.assigneeType == ApprovalType.assign_role) //指定角色
            {
                //var users = _revelanceRepository.Get(Define.USERROLE, false, node.properties.approvers);

                //makerList = GenericHelpers.ArrayToString(users, makerList);
            }
            else if (node.properties.assigneeType == ApprovalType.assign_leader)//指定负责人
            {
            }
            else if (node.properties.assigneeType == ApprovalType.assign_org)//指定部门
            {
            }
        }
        else //如果没有设置节点信息，默认所有人都可以审核
        {
            makerList = ApprovalType.all;
        }

        return makerList;
    }

    #endregion 获取节点的流程审核者

    #region 流程实例预览

    /// <summary>
    /// 获取审批节点链路用于预览显示
    /// </summary>
    /// <remarks>审批流程实例审批人预览</remarks>
    /// <param name="runtime"></param>
    /// <returns></returns>
    public List<FlowNode> GetApprovalNodeList(FlowRuntime runtime)
    {
        var list = new List<FlowNode>();
        var startNodeId = runtime.startNodeId;
        FlowNode currentNode = runtime.GetFlowNodeById(runtime.startNodeId);
        currentNode.nodeDisplayName = runtime.applyUserName;
        list.Add(currentNode);

        while (!string.IsNullOrWhiteSpace(startNodeId))
        {
            var node = runtime.GetNextApprovalNode(startNodeId);
            if (node == null) { break; }
            node.nodeFrom = startNodeId;
            node.prevId = new List<string>() { startNodeId };
            startNodeId = node.nodeId;
            if (!list.Any(c => c.nodeId == node.nodeId))
            {
                list.Add(node);
            }
        }
        return GetApprovalNodeRebuild(list);
    }

    private List<FlowNode> GetApprovalNodeRebuild(List<FlowNode> nodes)
    {
        foreach (var item in nodes)
        {
            if (string.IsNullOrWhiteSpace(item.nodeFrom)) { continue; }
            var node = nodes.First(c => c.nodeId == item.nodeFrom);
            node.nodeTo = new List<string>() { item.nodeId };
        }
        return nodes;
    }

    #endregion 流程实例预览

    #region 获取条件节点的判断条件

    /// <summary>
    /// 获取条件节点的判断条件
    /// </summary>
    /// <remarks>用于流程实例中节点条件的判断</remarks>
    /// <typeparam name="T"></typeparam>
    /// <param name="model"></param>
    /// <param name="jsonFrmData"></param>
    /// <returns></returns>
    public string GetFrmData<T>(T model, string jsonFrmData) where T : class
    {
        if (model == null) { return string.Empty; }
        var confs = Json.ToList<ConditionFrmDataConf>(jsonFrmData);
        if (confs == null || !confs.Any()) { return string.Empty; }

        var dic = new Dictionary<string, string>();
        foreach (PropertyInfo p in model.GetType().GetProperties())
        {
            string name = p.Name.ToLower();
            string value = p.GetValue(model)?.ToString();
            if (!string.IsNullOrEmpty(value) && confs.Any(c => c.fieldName.ToLower() == name))
            {
                dic.Add(name, value);
            }
        }
        return JsonHelper.IsJsonNullOrEmpty(dic.ToJson()) ? string.Empty : dic.ToJson();
    }

    #endregion 获取条件节点的判断条件

    #region 检查审核流程的状态

    /// <summary>
    /// 检查是否有该实例的审批权限
    /// </summary>
    /// <param name="instanceId">实例Id</param>
    /// <returns>true 有权限 false 没有权限</returns>
    public bool CheckApprovalAuth(string instanceId)
    {
        var flowInstance = _flowinstanceRepository.GetByKey(instanceId);
        var makerList = GenericHelpers.StringToArray(flowInstance.MakerList);
        if (makerList.Any(c => c == user.UserId))
        {
            return true;
        }
        return false;
    }

    /// <summary>
    /// 检查是否重复审批
    /// </summary>
    /// <param name="instanceId">实例Id</param>
    /// <returns>true 已审批 false 未审批</returns>
    public bool CheckRepeatApproval(string instanceId)
    {
        var records = from flow in _flowinstanceRepository.EntitiesAsNoTracking
                      join recd in _approvalRecordRepository.EntitiesAsNoTracking
                       on flow.Id equals recd.InstanceId
                      where !flow.IsDeleted
                      select recd;

        if (records.Any(c => c.ApprovalUserId == user.UserId))
        {
            return true;
        }
        return false;
    }

    #endregion 检查审核流程的状态

    #region 私有方法

    /// <summary>
    ///  流程转到申请人节点
    /// </summary>
    private void CheckRunToApplyUser(Fw_Flowinstance flowInstance, FlowRuntime wfruntime)
    {
        flowInstance.PreviousId = wfruntime.currentNodeId;
        flowInstance.ActivityId = wfruntime.startNodeId;
        flowInstance.ActivityType = (int)FlowNodeType.Start;
        flowInstance.ActivityName = wfruntime.Nodes[wfruntime.startNodeId]?.nodeName ?? Define.FlowEndTitle;
        flowInstance.MakerList = flowInstance.ApplyUserId;
    }

    /// <summary>
    ///  流程转到下一审批人节点
    /// </summary>
    private void CheckRunToNextApprovalNode(Fw_Flowinstance flowInstance, FlowRuntime wfruntime)
    {
        //流转到下一步
        flowInstance.PreviousId = flowInstance.ActivityId;
        flowInstance.ActivityId = wfruntime.nextNodeId;
        flowInstance.ActivityType = wfruntime.nextNode?.nodeType ?? (int)FlowNodeType.End;
        flowInstance.ActivityName = wfruntime.nextNode?.nodeName ?? Define.FlowEndTitle;
        flowInstance.MakerList = wfruntime.nextNodeId != Define.FlowEndCode ? GetNextMakers(wfruntime) : flowInstance.ApplyUserId;
    }

    /// <summary>
    /// 工作流程流转记录
    /// </summary>
    private void AddTransHistory(FlowRuntime wfruntime)
    {
        _flowTransitionRepository.Insert(new Fw_FlowinstanceTransitionHistory
        {
            Id = SnowflakeId.Default().NextId().ToString(),
            InstanceId = wfruntime.flowInstanceId,
            CreateBy = user.UserId + user.UserName,
            FromNodeId = wfruntime.currentNodeId,
            FromNodeName = wfruntime.currentNode.nodeName.ParseToString(),
            FromNodeType = (int)wfruntime.currentNodeType,
            ToNodeId = wfruntime.nextNodeId,
            ToNodeName = wfruntime.nextNode?.nodeName.ParseToString(),
            ToNodeType = wfruntime.nextNode?.nodeType ?? (int)FlowNodeType.End,
            IsFinish = wfruntime.nextNodeId == Define.FlowEndCode ? FlowInstanceStatus.Finished : FlowInstanceStatus.Running,
            TransitionSate = 0
        });
    }

    /// <summary>
    /// 添加 操作记录
    /// </summary>
    private void AddOperationHistory(FlowRuntime wfruntime, OptionResultTag optTag)
    {

        _flowOptionRepository.Insert(new Fw_FlowinstanceOperationHistory
        {
            Id = SnowflakeId.Default().NextId().ToString(),
            InstanceId = wfruntime.flowInstanceId,
            CounterSign = wfruntime.counterSign,
            CurrentNodeId = wfruntime.currentNodeId,
            FrmData = wfruntime.FrmData,
            Taged = optTag.Taged,
            OperateUserId = optTag.UserId,
            OperateUserName = optTag.UserName,
            Content = optTag.Description,
            TagedTime = optTag.TagedTime
        });
    }

    /// <summary>
    /// 添加 审批记录
    /// </summary>
    private void AddApprovalRecordHistory(FlowRuntime wfruntime, OptionResultTag optTag, Action<OptionResultTag> action = null)
    {
        if (action != null)
        {
            action(optTag);
        }
        _approvalRecordRepository.Insert(new Fw_ApprovalRecord
        {
            Id = SnowflakeId.Default().NextId().ToString(),
            InstanceId = wfruntime.flowInstanceId,
            CurrentNodeId = wfruntime.currentNodeId,
            CounterSign = wfruntime.counterSign,
            ApprovalUserId = optTag.UserId,
            ApprovalUserName = optTag.UserName,
            ApprovalReslut = optTag.Taged,
            ApprovalRemark = optTag.Description,
            ApprovalTime = optTag.TagedTime
        });
    }

    #endregion 私有方法
}